/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util.actions;
import java.beans.*;
import org.openide.windows.TopComponent;
import org.openide.nodes.Node;
import org.openide.util.RequestProcessor;
/** An action which can listen to the activated node selection.
* This means that the set of nodes active in a window
* may change the enabled state of the action according to {@link #enable}.
*
* @author Jan Jancura, Ian Formanek, Jaroslav Tulach
*/
public abstract class NodeAction extends CallableSystemAction {
static final long serialVersionUID =-5672895970450115226L;
/** the listener for this action */
private static final String PROP_LISTENER = "listener"; // NOI18N
/* Initialize the listener.
*/
protected void initialize () {
super.initialize ();
NodesL l = new NodesL (getClass ());
putProperty(PROP_LISTENER, l);
}
/* Adds listener to registry.
*/
protected void addNotify () {
// initializes the listener
NodesL l = (NodesL)getProperty (PROP_LISTENER);
l.setActive (true);
}
/* Removes listener to changes of activated nodes */
protected void removeNotify () {
NodesL l = (NodesL)getProperty (PROP_LISTENER);
l.setActive (false);
}
/** Test for enablement based on {@link #enable}.
* You probably ought not override this, except possibly
* to call the super method and add an additional check.
* @return <code>true</code> to enable
*/
public boolean isEnabled () {
NodesL l = (NodesL)getProperty (PROP_LISTENER);
if (!l.isActive ()) {
l.checkEnabled (this);
}
return super.isEnabled ();
}
/* Implementation of method of javax.swing.Action interface.
* Checks if the source of event is node and if so, it executes
* performAction (thatNode). Otherwise simply calls
* performAction ().
*
* @param ev event where to find source
*/
public void actionPerformed (java.awt.event.ActionEvent ev) {
Object s = ev == null ? null : ev.getSource ();
if (s instanceof Node) {
performAction (new Node[] { (Node) s });
} else {
performAction ();
}
}
/** Performs the action.
* In the default implementation, calls {@link #performAction(Node[])}.
* In general you need not override this.
*/
public void performAction() {
performAction (getActivatedNodes ());
}
/** Get the currently activated nodes.
* @return the nodes (may be empty but not <code>null</code>)
*/
public final Node[] getActivatedNodes () {
return TopComponent.getRegistry ().getActivatedNodes ();
}
/** Specify the behavior of the action when a window with no
* activated nodes is selected.
* If the action should then be disabled,
* return <code>false</code> here; if the action should stay in the previous state,
* return <code>true</code>.
* <p>Note that {@link #getActivatedNodes} and {@link #performAction} are still
* passed the set of selected nodes from the old window, if you keep this feature on.
* This is useful, e.g., for an action like Compilation which should remain active
* even if the user switches to a window like the Output Window that has no associated nodes;
* then running the action will still use the last selection from e.g. an Explorer window
* or the Editor, if there was one to begin with.
*
* @return <code>true</code> in the default implementation
*/
protected boolean surviveFocusChange() {
return true;
}
/**
* Perform the action based on the currently activated nodes.
* Note that if the source of the event triggering this action was itself
* a node, that node will be the sole argument to this method, rather
* than the activated nodes.
*
* @param activatedNodes current activated nodes, may be empty but not <code>null</code>
*/
protected abstract void performAction (Node[] activatedNodes);
/**
* Test whether the action should be enabled based
* on the currently activated nodes.
*
* @param activatedNodes current activated nodes, may be empty but not <code>null</code>
* @return <code>true</code> to be enabled, <code>false</code> to be disabled
*/
protected abstract boolean enable (Node[] activatedNodes);
/** Node listener to check whether the action is enabled or not
*/
private static final class NodesL
implements PropertyChangeListener, Runnable {
/** the class we work with */
private Class clazz;
/** active or not */
private boolean active;
/** a task to delay a while updating of action state */
private RequestProcessor.Task task;
/** delay before updating */
private static final int DELAY = 150;
/** Constructor that checks the current state
*/
public NodesL (Class clazz) {
this.clazz = clazz;
task = RequestProcessor.createRequest(this);
task.setPriority (Thread.MAX_PRIORITY - 1);
}
/** Activates/passivates the listener.
*/
void setActive (boolean active) {
if (active != this.active) {
this.active = active;
if (active) {
TopComponent.Registry r = TopComponent.getRegistry();
r.addPropertyChangeListener (this);
checkEnabled (action ());
} else {
TopComponent.getRegistry().removePropertyChangeListener(this);
}
}
}
/** Is the listener active?
*/
boolean isActive () {
return active;
}
/** Property change listener.
*/
public void propertyChange (PropertyChangeEvent ev) {
NodeAction a = action ();
if (a.surviveFocusChange () && TopComponent.Registry.PROP_ACTIVATED_NODES.equals (ev.getPropertyName ())) {
task.schedule (DELAY);
return;
}
if (!a.surviveFocusChange () && TopComponent.Registry.PROP_CURRENT_NODES.equals (ev.getPropertyName ())) {
task.schedule (DELAY);
return;
}
}
/** Updates the state of the action.
*/
public void run() {
NodeAction a = action ();
checkEnabled (a);
}
/** test on deactivating nodes *
public void nodesDeactivated (NodesEvent event) {
Node[] n = event.getActivatedNodes();
NodeAction a = action ();
if (a == null) return;
if (n != null) {
a.setEnabled (a.enable (nodes = n));
} else {
if (!a.surviveFocusChange ()) {
a.setEnabled (false);
}
}
}
*/
/** Getter for activated nodes
* @return array
*/
public Node[] getActivatedNodes (boolean survive) {
if (survive) {
return TopComponent.getRegistry ().getActivatedNodes ();
} else {
return TopComponent.getRegistry ().getCurrentNodes ();
}
}
/** Obtains the action.
* @return the node action we belong to or null if there is none
*/
private NodeAction action () {
return (NodeAction)findObject (clazz);
}
/** Checks the state of the action.
*/
void checkEnabled (NodeAction a) {
// no action no work
if (a == null) return;
Node[] n = getActivatedNodes (a.surviveFocusChange ());
boolean b = n != null && a.enable (n);
a.setEnabled (b);
}
}
}
/*
* Log
* 3 Tuborg 1.2 07/29/98 Jaroslav Tulach Removed internal field
* because of quick
* initialization.
*
* 2 Tuborg 1.1 06/15/98 Ian Formanek
* 1 Tuborg 1.0 06/11/98 David Peroutka
* $
* Beta Change History:
* 0 Tuborg 0.13 --/--/98 Jaroslav Tulach minimal class
* 0 Tuborg 0.14 --/--/98 Jan Formanek surviveFocusChange
* 0 Tuborg 0.15 --/--/98 Jan Jancura getActivatedNodes method added.
*/